package org.feng.framework;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 应用上下文实现
 *
 * @version v1.0
 * @author: fengjinsong
 * @date: 2022年04月09日 14时41分
 */
public class DefaultAnnotationApplicationContext implements ApplicationContext {

    /**
     * 配置类
     */
    private final Class<?> configClass;

    private static final String PROTOTYPE = "prototype";
    private static final String SINGLETON = "singleton";

    /**
     * beanName 和 bean 定义的映射
     */
    private static final Map<String, BeanDefinition> BEAN_DEFINITION_MAP = new HashMap<>(32);
    /**
     * 单例池
     */
    private static final Map<String, Object> SINGLETON_MAP = new ConcurrentHashMap<>();

    /**
     * 后置处理器
     */
    private static final List<BeanPostProcessor> BEAN_POST_PROCESSORS = new ArrayList<>();

    public DefaultAnnotationApplicationContext(Class<?> configClass) {
        this.configClass = configClass;
        // 校验配置、注解使用是否正常
        check(configClass);

        // 扫描包，获得bean定义
        scanConfig();

        // 非懒加载、单例bean生成实例
        createNonLazySingletonBean();
    }


    @Override
    public Object getBean(String name) {
        BeanDefinition beanDefinition = BEAN_DEFINITION_MAP.get(name);
        if (beanDefinition != null) {
            // 单例bean
            if (SINGLETON.equals(beanDefinition.getScope())) {
                // 处理懒加载
                if (beanDefinition.isLazy()) {
                    if (SINGLETON_MAP.containsKey(name)) {
                        return SINGLETON_MAP.get(name);
                    }
                    Object instance = creatInstance(name, beanDefinition);
                    SINGLETON_MAP.put(name, instance);
                    return instance;
                }
                // 非懒加载从单例池中取
                return SINGLETON_MAP.get(name);
            }

            // 多例bean
            if (PROTOTYPE.equals(beanDefinition.getScope())) {
                return creatInstance(name, beanDefinition);
            }
        }
        return null;
    }

    @Override
    public <T> T getBean(String name, Class<T> clazz) {
        BeanDefinition beanDefinition = BEAN_DEFINITION_MAP.get(name);
        if (beanDefinition != null) {
            // 单例bean
            if (SINGLETON.equals(beanDefinition.getScope()) && clazz == beanDefinition.getBeanClass()) {
                // 处理懒加载
                if (beanDefinition.isLazy()) {
                    if (SINGLETON_MAP.containsKey(name)) {
                        return (T) SINGLETON_MAP.get(name);
                    }
                    Object instance = creatInstance(name, beanDefinition);
                    SINGLETON_MAP.put(name, instance);
                    return (T) instance;
                }
                // 非懒加载从单例池中取
                return (T) SINGLETON_MAP.get(name);
            }

            // 多例bean
            if (PROTOTYPE.equals(beanDefinition.getScope()) && clazz == beanDefinition.getBeanClass()) {
                return (T) creatInstance(name, beanDefinition);
            }
        }
        return null;
    }

    /**
     * 扫描配置并注册bean定义
     */
    private void scanConfig() {
        // 获取要扫描的包名
        ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
        String packageName = componentScan.value();
        // 获取包名下的完整类名
        List<String> fullClassNameList = parsePackageToFullClassNameList(packageName);
        for (String className : fullClassNameList) {
            try {
                // 通过类名加载类
                Class<?> aClass = DefaultAnnotationApplicationContext.class.getClassLoader().loadClass(className);
                // 该类使用了 Component 注解
                if (aClass.isAnnotationPresent(Component.class)) {
                    BeanDefinition beanDefinition = new BeanDefinition();
                    // 设置bean对应的类
                    beanDefinition.setBeanClass(aClass);
                    // 当前bean是后置处理器的实现类：当实现了BeanPostProcessor接口时
                    if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                        BEAN_POST_PROCESSORS.add((BeanPostProcessor) aClass.getDeclaredConstructor().newInstance());
                    }
                    // 懒加载
                    if (aClass.isAnnotationPresent(Lazy.class)) {
                        beanDefinition.setLazy(true);
                    }

                    // 判断是多例bean还是单例bean
                    if (aClass.isAnnotationPresent(Scope.class)) {
                        Scope scope = aClass.getAnnotation(Scope.class);
                        String scopeValue = scope.value();
                        if (scopeValue == null || "".equals(scopeValue) || SINGLETON.equals(scopeValue)) {
                            beanDefinition.setScope(SINGLETON);
                        }
                        // 多例bean
                        if (PROTOTYPE.equals(scopeValue)) {
                            beanDefinition.setScope(PROTOTYPE);
                        }
                    } else {
                        // 单例bean
                        beanDefinition.setScope(SINGLETON);
                    }

                    // 将 key=beanName，value=beanDefinition的映射存储到容器中
                    Component component = aClass.getAnnotation(Component.class);
                    String beanName = fixBeanName(component.value(), aClass);
                    BEAN_DEFINITION_MAP.put(beanName, beanDefinition);
                }
            } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 非懒加载、单例bean生成实例，注册到容器
     */
    private void createNonLazySingletonBean() {
        BEAN_DEFINITION_MAP.forEach((beanName, beanDefinition) -> {
            // 非懒加载 并且是单例bean
            if (!beanDefinition.isLazy() && SINGLETON.equals(beanDefinition.getScope())) {
                Object instance = creatInstance(beanName, beanDefinition);
                SINGLETON_MAP.put(beanName, instance);
            }
        });
    }


    /**
     * 创建Bean
     *
     * @param beanDefinition bean定义
     * @param beanName       bean名称
     * @return 实例bean
     */
    private Object creatInstance(String beanName, BeanDefinition beanDefinition) {
        Object instance = null;
        Class<?> beanClass = beanDefinition.getBeanClass();
        try {
            instance = beanClass.getDeclaredConstructor().newInstance();

            // 属性赋值之前处理
            for (BeanPostProcessor beanPostProcessor : BEAN_POST_PROCESSORS) {
                beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            // 属性填充
            Field[] declaredFields = beanClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(Autowird.class)) {
                    Object fieldBean = getBean(declaredField.getName());
                    declaredField.setAccessible(true);
                    declaredField.set(instance, fieldBean);
                }
            }

            // 设置beanName
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }
            // 属性设置后，增加的操作
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // bean后置处理里
            for (BeanPostProcessor beanPostProcessor : BEAN_POST_PROCESSORS) {
                beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 解析传入的包名，获取其下的所有的类名（包深度是20）
     *
     * @param packageName 包名
     * @return 包名下的完整类名
     */
    private List<String> parsePackageToFullClassNameList(String packageName) {
        ClassLoader classLoader = DefaultAnnotationApplicationContext.class.getClassLoader();
        // 获取资源文件路径
        String path = packageName.replaceAll("\\.", "/");
        URL resource = classLoader.getResource(path);
        // 解析当前的类路径
        assert resource != null;
        try {
            // 获取包下的完整类名，文件深度为20
            return Files.walk(Path.of(resource.toURI()), 20)
                    .map(Path::toFile)
                    .filter(file -> file.getName().endsWith(".class"))
                    .map(File::getAbsolutePath)
                    .map(filePath -> filePath.replaceAll("\\\\", "/"))
                    .map(filePath -> filePath.substring(filePath.indexOf(path), filePath.indexOf(".class")))
                    .map(filePath -> filePath.replaceAll("/", "."))
                    .collect(Collectors.toList());
        } catch (IOException | URISyntaxException e) {
            e.printStackTrace();
        }
        return Collections.emptyList();
    }

    /**
     * 将类名首字母小写作为beanName的默认值
     *
     * @param beanName 实例在容器中的名称，可能为空
     * @param aClass   当前实例的类
     * @return 修正过的实例在容器中的名称
     */
    private String fixBeanName(String beanName, Class<?> aClass) {
        if (beanName == null || "".equals(beanName)) {
            String simpleName = aClass.getSimpleName();
            String firstLetter = simpleName.substring(0, 1);
            return firstLetter.toLowerCase(Locale.ROOT) + simpleName.substring(1);
        }
        return beanName;
    }

    /**
     * 校验注解 Configuration、ComponentScan 是否有在使用
     *
     * @param configClass 配置类
     */
    private void check(Class<?> configClass) {
        // 当前配置类必须有 Configuration注解
        boolean isConfigClass = configClass.isAnnotationPresent(Configuration.class);
        if (!isConfigClass) {
            throw new RuntimeException("请给配置类上加 Configuration 注解");
        }
        // 当前配置类必须有 ComponentScan注解
        boolean hasComponentScan = configClass.isAnnotationPresent(ComponentScan.class);
        if (!hasComponentScan) {
            throw new RuntimeException("请给配置类上加 ComponentScan 注解");
        }
    }
}
